<?php
// ------------------------------------------------------------------------
// |@Author       : Jarmin <edshop@qq.com>
// |@----------------------------------------------------------------------
// |@Date         : 2025-04-18 08:14:45
// |@----------------------------------------------------------------------
// |@LastEditTime : 2025-04-19 09:26:06
// |@----------------------------------------------------------------------
// |@LastEditors  : Jarmin <jarmin@ladmin.cn>
// |@----------------------------------------------------------------------
// |@Description  : 
// |@----------------------------------------------------------------------
// |@FilePath     : ImageVo.php
// |@----------------------------------------------------------------------
// |@Copyright (c) 2025 http://www.ladmin.cn   All rights reserved. 
// ------------------------------------------------------------------------

declare(strict_types=1);

namespace think\captcha\domain\Vo;

use think\captcha\utils\MathUtils;
use Intervention\Image\ImageManager;

abstract class ImageVo
{
    /**
     * @var Image
     */
    public $image;

    public $src;

    private $pickMaps = [];

    private $finishCallback;

    // 新增性能优化相关属性
    private $batchPixelCache = [];
    private $memoryUsage = 0;
    private $maxMemoryLimit = 67108864; // 64MB内存限制

    public function __construct($src)
    {
        $this->src = $src;
        $this->initImage($src);
    }

    public function initImage($src)
    {
        $this->image = ImageManager::gd()->read($src);
    }

    /**
     * 批量获取像素颜色 - 性能优化核心方法
     * @param array $coordinates 坐标数组 [[x1,y1], [x2,y2], ...]
     * @return array 颜色数组
     */
    public function getBatchPixels(array $coordinates): array
    {
        $colors = [];
        $uncachedCoords = [];

        // 首先从缓存中获取已有的像素
        foreach ($coordinates as $coord) {
            [$x, $y] = $coord;
            if (isset($this->pickMaps[$x][$y])) {
                $colors["{$x}_{$y}"] = $this->pickMaps[$x][$y];
            } else {
                $uncachedCoords[] = $coord;
            }
        }

        // 批量获取未缓存的像素
        if (!empty($uncachedCoords)) {
            foreach ($uncachedCoords as $coord) {
                [$x, $y] = $coord;
                $color = $this->image->pickColor($x, $y);
                $rgbaColor = [
                    $color->red()->value(),
                    $color->green()->value(),
                    $color->blue()->value(),
                    $color->alpha()->normalize()
                ];
                $this->pickMaps[$x][$y] = $rgbaColor;
                $colors["{$x}_{$y}"] = $rgbaColor;
            }
        }

        return $colors;
    }

    /**
     * 批量设置像素颜色 - 性能优化核心方法
     * @param array $pixelData 像素数据 [['x'=>x, 'y'=>y, 'color'=>[r,g,b,a]], ...]
     */
    public function setBatchPixels(array $pixelData): void
    {
        // 批量设置像素，减少函数调用次数
        foreach ($pixelData as $pixel) {
            $x = $pixel['x'];
            $y = $pixel['y'];
            $color = $pixel['color'];
            
            $colorString = sprintf(
                'rgba(%d,%d,%d,%.2f)',
                $color[0], $color[1], $color[2], $color[3]
            );
            
            $this->image->drawPixel($x, $y, $colorString);
        }
    }

    /**
     * 获取图片中某一个位置的rgba值
     * @param $x
     * @param $y
     * @return array
     */
    public function getPickColor($x, $y): array
    {
        if (!isset($this->pickMaps[$x][$y])) {
            $color = $this->image->pickColor($x, $y);
            $this->pickMaps[$x][$y] = [
                $color->red()->value(),
                $color->green()->value(),
                $color->blue()->value(),
                $color->alpha()->normalize()
            ];
            
            // 内存监控
            $this->memoryUsage += 16; // 估算每个像素16字节
            if ($this->memoryUsage > $this->maxMemoryLimit) {
                $this->cleanupPixelCache();
            }
        }
        return $this->pickMaps[$x][$y];
    }

    /**
     * 清理像素缓存以释放内存
     */
    private function cleanupPixelCache(): void
    {
        // 保留最近使用的50%像素缓存
        $count = count($this->pickMaps);
        if ($count > 1000) {
            $keysToRemove = array_slice(array_keys($this->pickMaps), 0, intval($count * 0.5));
            foreach ($keysToRemove as $key) {
                unset($this->pickMaps[$key]);
            }
            $this->memoryUsage = intval($this->memoryUsage * 0.5);
        }
    }

    /**
     * 设置图片指定位置的颜色值
     */
    public function setPixel($color, $x, $y)
    {
        // 将RGBA数组转换为CSS颜色字符串格式
        $colorString = sprintf(
            'rgba(%d,%d,%d,%.2f)',
            $color[0], // R
            $color[1], // G
            $color[2], // B
            $color[3]  // A
        );
        
        $this->image->drawPixel($x, $y, $colorString);
    }

    /**
     * @param int $x
     * @param int $y
     * @return array
     */
    public function getBlurValue(int $x, int $y): array
    {
        $image = $this->image;
        $red = [];
        $green = [];
        $blue = [];
        $alpha = [];
        foreach ([[0, 1], [0, -1],[1, 0], [-1, 0],[1, 1], [1, -1],[-1, 1], [-1, -1]] as $distance) {
            $pointX = $x + $distance[0];
            $pointY = $y + $distance[1];
            if ($pointX < 0 || $pointX >= $image->width() || $pointY < 0 || $pointY >= $image->height()) {
                continue;
            }
            [$r, $g, $b, $a] = $this->getPickColor($pointX, $pointY);
            $red[] = $r;
            $green[] = $g;
            $blue[] = $b;
            $alpha[] = $a;
        }
        return [MathUtils::avg($red), MathUtils::avg($green), MathUtils::avg($blue), MathUtils::avg($alpha)];
    }

    /**
     * 是否不透明
     * @param $x
     * @param $y
     * @return bool
     */
    public function isOpacity($x, $y): bool
    {
        return $this->getPickColor($x, $y)[3] > 0.5;
    }

    /**
     * 是否为边框
     * @param bool $isOpacity
     * @param int $x
     * @param int $y
     * @return bool
     */
    public function isBoundary(bool $isOpacity, int $x, int $y): bool
    {
        $image = $this->image;
        if ($x >= $image->width() - 1 || $y >= $image->height() - 1) {
            return false;
        }
        $right = [$x + 1, $y];
        $down = [$x, $y + 1];
        if ($isOpacity && !$this->isOpacity(...$right) || !$isOpacity && $this->isOpacity(...$right) || $isOpacity && !$this->isOpacity(...$down) || !$isOpacity && $this->isOpacity(...$down)) {
            return true;
        }
        return false;
    }

    /**
     * 模糊图片
     * @param $targetX
     * @param $targetY
     */
    public function vagueImage($targetX, $targetY)
    {
        $blur = $this->getBlurValue($targetX, $targetY);
        $this->setPixel($blur, $targetX, $targetY);
    }

    /**
     * @return array
     */
    public function getPickMaps(): array
    {
        return $this->pickMaps;
    }

    /**
     * @param array $pickMaps
     */
    public function setPickMaps(array $pickMaps): void
    {
        $this->pickMaps = $pickMaps;
    }

    /**
     * 提前初始化像素
     */
    public function preparePickMaps()
    {
        $width = $this->image->width();
        $height = $this->image->height();
        for ($x = 0; $x < $width; $x++) {
            for ($y = 0; $y < $height; $y++) {
                $this->getPickColor($x, $y);
            }
        }
    }

    public function setFinishCallback($finishCallback)
    {
        $this->finishCallback = $finishCallback;
    }

    public function __destruct()
    {
        if (!empty($this->finishCallback) && $this->finishCallback instanceof \Closure) {
            ($this->finishCallback)($this);
        }
    }
}
